package com.zzyl.serve.service.impl;

import java.time.LocalDateTime;
import java.util.List;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.ai.AIModelInvoker;
import com.zzyl.common.constant.RedisKeyConstants;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.IDCardUtils;
import com.zzyl.common.utils.PDFUtil;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.oss.AliyunOSSOperator;
import com.zzyl.serve.domain.Elder;
import com.zzyl.serve.dto.HealthAssessmentDto;
import com.zzyl.serve.service.IElderService;
import com.zzyl.serve.vo.HealthReportVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.serve.mapper.HealthAssessmentMapper;
import com.zzyl.serve.domain.HealthAssessment;
import com.zzyl.serve.service.IHealthAssessmentService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;

import static com.zzyl.common.constant.RedisKeyConstants.HEALTH_ASSESSMENT_KEY;

/**
 * 健康评估Service业务层处理
 * 
 * @author chenming
 * @date 2025-05-04
 */
@Service
public class HealthAssessmentServiceImpl extends ServiceImpl<HealthAssessmentMapper, HealthAssessment> implements IHealthAssessmentService
{
    @Autowired
    private HealthAssessmentMapper healthAssessmentMapper;
    @Autowired
    private AliyunOSSOperator aliyunOSSService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private AIModelInvoker aIModelInvoker;
    @Autowired
    private IElderService elderService;

    /**
     * 查询健康评估
     * 
     * @param id 健康评估主键
     * @return 健康评估
     */
    @Override
    public HealthAssessment selectHealthAssessmentById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询健康评估列表
     * 
     * @param healthAssessment 健康评估
     * @return 健康评估
     */
    @Override
    public List<HealthAssessment> selectHealthAssessmentList(HealthAssessment healthAssessment)
    {
        return healthAssessmentMapper.selectHealthAssessmentList(healthAssessment);
    }

    /**
     * 新增健康评估
     * @return 结果
     */
    /*@Override
    public Long insertHealthAssessment(HealthAssessmentDto dto)
    {
        // 1. 设计prompt：（结合Redis中的内容 + 模板 得到prompt）
        String key = HEALTH_ASSESSMENT_KEY + dto.getIdCard();
        String text = (String) redisTemplate.opsForValue().get(key);

        if (StringUtils.isEmpty(text)){
            // 如果没有得到体检信息，我们抛出异常，终止运行
            throw new BaseException("体检信息为空，请先上传体检报告");
        }

        String prompt = "请以一个专业医生的视角来分析这份体检报告，报告中包含了一些异常数据，我需要您对这些数据进行解读，并给出相应的健康建议。\n" +
                "体检内容如下：\n" +
                text
                + "\n" +
                "要求：\n" +
                "1. 提取体检报告中的“总检日期”；\n" +
                "2. 通过临床医学、疾病风险评估模型和数据智能分析，给该用户的风险等级和健康指数给出结果。风险等级(输出为中文)分为：健康、提示、风险、危险、严重危险。健康指数范围为0至100分；\n" +
                "3. 根据用户身体各项指标数据，详细说明该用户各项风险等级的占比是多少，最多保留两位小数。结论格式：该用户健康占比20.00%，提示占比20.00%，风险占比20%，危险占比20%，严重危险占比20%；\n" +
                "4. 对于体检报告中的异常数据，请列出（异常数据的结论、体检项目名称、检查结果、参考值、单位、异常解读、建议）这8字段。解读异常数据，解决这些数据可能代表的健康问题或风险。分析可能的原因，包括但不限于生活习惯、饮食习惯、遗传因素等。基于这些异常数据和可能的原因，请给出具体的健康建议，包括饮食调整、运动建议、生活方式改变以及是否需要进一步检查或治疗等。\n" +
                "结论格式：异常数据的结论：肥胖，体检项目名称：体重指数BMI，检查结果：29.2，参考值>24，单位：-。异常解读：体重超标包括超重与肥胖。体重指数（BMI）=体重（kg）/身⾼（m）的平⽅，BMI≥24为超重，BMI≥28为肥胖；男性腰围≥90cm和⼥性腰围≥85cm为腹型肥胖。体重超标是⼀种由多因素（如遗传、进⻝油脂较多、运动少、疾病等）引起的慢性代谢性疾病，尤其是肥胖，已经被世界卫⽣组织列为导致疾病负担的⼗⼤危险因素之⼀。AI建议：采取综合措施预防和控制体重，积极改变⽣活⽅式，宜低脂、低糖、⾼纤维素膳⻝，多⻝果蔬及菌藻类⻝物，增加有氧运动。若有相关疾病（如⾎脂异常、⾼⾎压、糖尿病等）应积极治疗。\n" +
                "5. 根据这个体检报告的内容，分别给人体的8大系统打分，每项满分为100分，8大系统分别为：呼吸系统、消化系统、内分泌系统、免疫系统、循环系统、泌尿系统、运动系统、感官系统\n" +
                "6. 给体检报告做一个总结，总结格式：体检报告中尿蛋⽩、癌胚抗原、⾎沉、空腹⾎糖、总胆固醇、⽢油三酯、低密度脂蛋⽩胆固醇、⾎清载脂蛋⽩B、动脉硬化指数、⽩细胞、平均红细胞体积、平均⾎红蛋⽩共12项指标提示异常，尿液常规共1项指标处于临界值，⾎脂、⾎液常规、尿液常规、糖类抗原、⾎清酶类等共43项指标提示正常，综合这些临床指标和数据分析：肾脏、肝胆、⼼脑⾎管存在隐患，其中⼼脑⾎管有“⾼危”⻛险；肾脏部位有“中危”⻛险；肝胆部位有“低危”⻛险。\n" +
                "\n" +
                "输出要求：\n" +
                "最后，将以上结果输出为JSON格式，不要包含其他的文字说明，所有的返回结果都是json，详细格式如下：\n" +
                "\n" +
                "{\n" +
                "  \"totalCheckDate\": \"YYYY-MM-DD\",\n" +
                "  \"healthAssessment\": {\n" +
//                "    \"riskLevel\": \"healthy/caution/risk/danger/severeDanger\",\n" +
                "    \"riskLevel\": \"健康/提示/风险/危险/严重危险\",\n" +
                "    \"healthIndex\": XX.XX\n" +
                "  },\n" +
                "  \"riskDistribution\": {\n" +
                "    \"healthy\": XX.XX,\n" +
                "    \"caution\": XX.XX,\n" +
                "    \"risk\": XX.XX,\n" +
                "    \"danger\": XX.XX,\n" +
                "    \"severeDanger\": XX.XX\n" +
                "  },\n" +
                "  \"abnormalData\": [\n" +
                "    {\n" +
                "      \"conclusion\": \"异常数据的结论\",\n" +
                "      \"examinationItem\": \"体检项目名称\",\n" +
                "      \"result\": \"检查结果\",\n" +
                "      \"referenceValue\": \"参考值\",\n" +
                "      \"unit\": \"单位\",\n" +
                "      \"interpret\":\"对于异常的结论进一步详细的说明\",\n" +
                "      \"advice\":\"针对于这一项的异常，给出一些健康的建议\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"systemScore\": {\n" +
                "    \"breathingSystem\": XX,\n" +
                "    \"digestiveSystem\": XX,\n" +
                "    \"endocrineSystem\": XX,\n" +
                "    \"immuneSystem\": XX,\n" +
                "    \"circulatorySystem\": XX,\n" +
                "    \"urinarySystem\": XX,\n" +
                "    \"motionSystem\": XX,\n" +
                "    \"senseSystem\": XX\n" +
                "  },\n" +
                "  \"summarize\": \"体检报告的总结\"\n" +
                "}";

        // 2. 调用千帆大模型进行请求
        String result = aIModelInvoker.qianfanInvoker(prompt);

        // 3. 解析返回的json数据，使用vo接收
        HealthReportVo healthReportVo = JSONUtil.toBean(result, HealthReportVo.class);

        // 4. 封装数据需要 HealthAssessment 对象属性
        HealthAssessment healthAssessment = new HealthAssessment();
        BeanUtils.copyProperties(dto, healthAssessment);

        // 封装 身份证号、age、性别、 出生日期
        healthAssessment.setIdCard(dto.getIdCard());
        healthAssessment.setAge(IDCardUtils.getAgeByIdCard(dto.getIdCard()));
        healthAssessment.setGender(IDCardUtils.getGenderFromIdCard(dto.getIdCard()));
        healthAssessment.setBirthDate(IDCardUtils.getBirthDateByIdCard(dto.getIdCard()));

        // 通过ai返回的数据，进行封装
        // 封装：日期、 健康评分、危险等级、 评估时间、报告总结、疾病风险、异常分析、健康系统分值
        healthAssessment.setTotalCheckDate(healthReportVo.getTotalCheckDate());
        healthAssessment.setHealthScore(healthReportVo.getHealthAssessment().getHealthIndex()+"");
        healthAssessment.setRiskLevel(healthReportVo.getHealthAssessment().getRiskLevel());
        healthAssessment.setAssessmentTime(LocalDateTime.now());
        healthAssessment.setReportSummary(healthReportVo.getSummarize());
        String diseaseRisk = JSONUtil.toJsonStr(healthReportVo.getRiskDistribution());
        healthAssessment.setDiseaseRisk(diseaseRisk);
        healthAssessment.setAbnormalAnalysis(JSONUtil.toJsonStr(healthReportVo.getAbnormalData()));
        healthAssessment.setSystemScore(JSONUtil.toJsonStr(healthReportVo.getSystemScore()));


        // 入住建议、护理等级、入住情况
        // 判断健康分数，如果低于60分不建议入住
        if (healthAssessment.getHealthScore().compareTo("60") < 0) {
            healthAssessment.setSuggestionForAdmission(1);
        } else {
            healthAssessment.setSuggestionForAdmission(0);
        }
        // 如果入住，继续根据分数去判断时候何种护理等级
        String nursingLevelName = getNursingLevelName(Double.valueOf(healthAssessment.getHealthScore()));
        healthAssessment.setNursingLevelName(nursingLevelName);

        // 入住情况 去查询数据库
        long count = elderService.count(
                new LambdaQueryWrapper<Elder>()
                        .eq(Elder::getIdCardNo, healthAssessment.getIdCard())
                        .eq(Elder::getStatus, 1)
        );
        if (count>0){
            // 入住情况(0:已入住，1:未入住)
            healthAssessment.setAdmissionStatus(0);
        }else {
            //
            healthAssessment.setAdmissionStatus(1);
        }


        // 5. 调用mapper进行插入
        this.save(healthAssessment);

        // 6. 数据库体检评测保存成功后，可以删除Redis缓存的体检信息：
        redisTemplate.delete(key);

        // 7. 返回id进行响应
        return healthAssessment.getId();
    }*/


    // 抽取方法写接口:新增健康评估
    /**
     * 新增健康评估
     *
     * @param dto
     * @return 结果
     */
    @Override
    public Long insertHealthAssessment(HealthAssessmentDto dto) {
        //1.设计prompt:(结合Redis中的内容+模板，得到prompt)
        String prompt = getPrompt(dto.getIdCard());
        //2.调用千帆大模型进行请求
        String result = aIModelInvoker.qianfanInvoker(prompt);
        // 3.解析返回的json数据，使用vo接收
        HealthReportVo healthReportVo = JSONUtil.toBean(result, HealthReportVo.class);
        // 4.使用setHealthAssessment方法，封装数据
        HealthAssessment healthAssessment = setHealthAssessment(healthReportVo, dto);

        //5. 判断健康分数，如果低于60分不建议入住
        if (healthAssessment.getHealthScore().compareTo("60") < 0) {
            //1 不建议入住
            healthAssessment.setSuggestionForAdmission(1);
        } else {
            //2 建议入住
            healthAssessment.setSuggestionForAdmission(0);
        }
        // 6.根据分数去判断时候何种护理等级
        healthAssessment.setNursingLevelName(getNursingLevelName(Double.valueOf(healthAssessment.getHealthScore())));
        // 7.入住情况 去查询数据库
        long count = elderService.count(
                new LambdaQueryWrapper<Elder>()
                        .eq(Elder::getIdCardNo, healthAssessment.getIdCard())
                        .eq(Elder::getStatus, 1)
        );
        if (count > 0) {
            //入住情况(0：已入住，1:未入住)
            healthAssessment.setAdmissionStatus(0);
        }else {
            healthAssessment.setAdmissionStatus(1);
        }
        // 8.调用mapper进行插入
        this.save(healthAssessment);
        // 9.数据保存成功后，可以删除Redis缓存的体检信息
        redisTemplate.delete(RedisKeyConstants.HEALTH_ASSESSMENT_KEY + dto.getIdCard());
        // 10.返回id进行响应
        return healthAssessment.getId();
    }

    /**
     * 抽取方法一： 设计prompt
     * @param idCard
     * @return
     */
    private String getPrompt(String idCard) {

        // 从Redis取出指定用户的体检报告文本信息
        String value = (String) redisTemplate.opsForValue().get(RedisKeyConstants.HEALTH_ASSESSMENT_KEY + idCard);

        // 健壮性处理
        if (StringUtils.isEmpty(value)) {
            throw new BaseException("该用户没有体检报告,请上传以后再进行智能评测");
        }

        // 拼接prompt
        String prompt = "请以一个专业医生的视角来分析这份体检报告，报告中包含了一些异常数据，我需要您对这些数据进行解读，并给出相应的健康建议。\n" +
                "体检内容如下：\n" +

                value +

                "\n" +
                "要求：\n" +
                "1. 提取体检报告中的“总检日期”；\n" +
                "2. 通过临床医学、疾病风险评估模型和数据智能分析，给该用户的风险等级和健康指数给出结果。风险等级分为：健康、提示、风险、危险、严重危险。健康指数范围为0至100分；\n" +
                "3. 根据用户身体各项指标数据，详细说明该用户各项风险等级的占比是多少，最多保留两位小数。结论格式：该用户健康占比20.00%，提示占比20.00%，风险占比20%，危险占比20%，严重危险占比20%；\n" +
                "4. 对于体检报告中的异常数据，请列出（异常数据的结论、体检项目名称、检查结果、参考值、单位、异常解读、建议）这8字段。解读异常数据，解决这些数据可能代表的健康问题或风险。分析可能的原因，包括但不限于生活习惯、饮食习惯、遗传因素等。基于这些异常数据和可能的原因，请给出具体的健康建议，包括饮食调整、运动建议、生活方式改变以及是否需要进一步检查或治疗等。\n" +
                "结论格式：异常数据的结论：肥胖，体检项目名称：体重指数BMI，检查结果：29.2，参考值>24，单位：-。异常解读：体重超标包括超重与肥胖。体重指数（BMI）=体重（kg）/身⾼（m）的平⽅，BMI≥24为超重，BMI≥28为肥胖；男性腰围≥90cm和⼥性腰围≥85cm为腹型肥胖。体重超标是⼀种由多因素（如遗传、进⻝油脂较多、运动少、疾病等）引起的慢性代谢性疾病，尤其是肥胖，已经被世界卫⽣组织列为导致疾病负担的⼗⼤危险因素之⼀。AI建议：采取综合措施预防和控制体重，积极改变⽣活⽅式，宜低脂、低糖、⾼纤维素膳⻝，多⻝果蔬及菌藻类⻝物，增加有氧运动。若有相关疾病（如⾎脂异常、⾼⾎压、糖尿病等）应积极治疗。\n" +
                "5. 根据这个体检报告的内容，分别给人体的8大系统打分，每项满分为100分，8大系统分别为：呼吸系统、消化系统、内分泌系统、免疫系统、循环系统、泌尿系统、运动系统、感官系统\n" +
                "6. 给体检报告做一个总结，总结格式：体检报告中尿蛋⽩、癌胚抗原、⾎沉、空腹⾎糖、总胆固醇、⽢油三酯、低密度脂蛋⽩胆固醇、⾎清载脂蛋⽩B、动脉硬化指数、⽩细胞、平均红细胞体积、平均⾎红蛋⽩共12项指标提示异常，尿液常规共1项指标处于临界值，⾎脂、⾎液常规、尿液常规、糖类抗原、⾎清酶类等共43项指标提示正常，综合这些临床指标和数据分析：肾脏、肝胆、⼼脑⾎管存在隐患，其中⼼脑⾎管有“⾼危”⻛险；肾脏部位有“中危”⻛险；肝胆部位有“低危”⻛险。\n" +
                "\n" +
                "输出要求：\n" +
                "最后，将以上结果输出为JSON格式，不要包含其他的文字说明，所有的返回结果都是json，详细格式如下：\n" +
                "\n" +
                "{\n" +
                "  \"totalCheckDate\": \"YYYY-MM-DD\",\n" +
                "  \"healthAssessment\": {\n" +
//                "    \"riskLevel\": \"healthy/caution/risk/danger/severeDanger\",\n" +
                "    \"riskLevel\": \"健康/提示/提示/危险/严重危险\",\n" +
                "    \"healthIndex\": XX.XX\n" +
                "  },\n" +
                "  \"riskDistribution\": {\n" +
                "    \"healthy\": XX.XX,\n" +
                "    \"caution\": XX.XX,\n" +
                "    \"risk\": XX.XX,\n" +
                "    \"danger\": XX.XX,\n" +
                "    \"severeDanger\": XX.XX\n" +
                "  },\n" +
                "  \"abnormalData\": [\n" +
                "    {\n" +
                "      \"conclusion\": \"异常数据的结论\",\n" +
                "      \"examinationItem\": \"体检项目名称\",\n" +
                "      \"result\": \"检查结果\",\n" +
                "      \"referenceValue\": \"参考值\",\n" +
                "      \"unit\": \"单位\",\n" +
                "      \"interpret\":\"对于异常的结论进一步详细的说明\",\n" +
                "      \"advice\":\"针对于这一项的异常，给出一些健康的建议\"\n" +
                "    }\n" +
                "  ],\n" +
                "  \"systemScore\": {\n" +
                "    \"breathingSystem\": XX,\n" +
                "    \"digestiveSystem\": XX,\n" +
                "    \"endocrineSystem\": XX,\n" +
                "    \"immuneSystem\": XX,\n" +
                "    \"circulatorySystem\": XX,\n" +
                "    \"urinarySystem\": XX,\n" +
                "    \"motionSystem\": XX,\n" +
                "    \"senseSystem\": XX\n" +
                "  },\n" +
                "  \"summarize\": \"体检报告的总结\"\n" +
                "}";


        return prompt;
    }

    /**
     * 抽取方法二，封装实体对象
     * @return
     */
    private HealthAssessment setHealthAssessment(HealthReportVo healthReportVo, HealthAssessmentDto dto) {
        //1.新增
        HealthAssessment healthAssessment=new HealthAssessment();
        //2.封装数据
        healthAssessment.setElderName(dto.getElderName());
        healthAssessment.setIdCard(dto.getIdCard());
        //生日,年龄，性别
        healthAssessment.setBirthDate(IDCardUtils.getBirthDateByIdCard(dto.getIdCard()));
        healthAssessment.setAge(IDCardUtils.getAgeByIdCard(dto.getIdCard()));
        healthAssessment.setGender(IDCardUtils.getGenderFromIdCard(dto.getIdCard()));

        //健康评分，危险等级，是否建议入住
        double healthScore = healthReportVo.getHealthAssessment().getHealthIndex();
        healthAssessment.setHealthScore(String.valueOf(healthScore));
        healthAssessment.setRiskLevel(healthReportVo.getHealthAssessment().getRiskLevel());
        healthAssessment.setSuggestionForAdmission(healthScore>=60?0:1);
        //推荐护理等级，入住情况，总检日期，体检机构，体检报告URL链接，评估时间，报告总结，疾病风险，异常分析，健康系统分值
        healthAssessment.setNursingLevelName(getNursingLevelName(healthScore));
        healthAssessment.setAdmissionStatus(1);
        healthAssessment.setTotalCheckDate(healthReportVo.getTotalCheckDate());
        healthAssessment.setPhysicalExamInstitution(dto.getPhysicalExamInstitution());
        healthAssessment.setPhysicalReportUrl(dto.getPhysicalReportUrl());
        healthAssessment.setAssessmentTime(LocalDateTime.now());
        healthAssessment.setReportSummary(healthReportVo.getSummarize());
        healthAssessment.setDiseaseRisk(JSONUtil.toJsonStr(healthReportVo.getRiskDistribution()));
        healthAssessment.setAbnormalAnalysis(JSONUtil.toJsonStr(healthReportVo.getAbnormalData()));
        healthAssessment.setSystemScore(JSONUtil.toJsonStr(healthReportVo.getSystemScore()));

        return healthAssessment;
    }

    /**
     * 根据分数获取护理等级
     * @param healthScore
     * @return
     */
    private String getNursingLevelName(double healthScore) {
        if(healthScore<0 || healthScore>100){
            throw new BaseException("健康评分不在0-100之间");
        }
        if (healthScore>=90) {
            return "三级护理";
        }else if (healthScore>=80){
            return "二级护理";
        }else if (healthScore>=70){
            return "一级护理";
        }else if(healthScore>=60){
            return "特级护理";
        }
        return "不建议入住";
    }

    /**
     * 修改健康评估
     * 
     * @param healthAssessment 健康评估
     * @return 结果
     */
    @Override
    public int updateHealthAssessment(HealthAssessment healthAssessment)
    {
        return updateById(healthAssessment) ? 1 : 0;
    }

    /**
     * 批量删除健康评估
     * 
     * @param ids 需要删除的健康评估主键
     * @return 结果
     */
    @Override
    public int deleteHealthAssessmentByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除健康评估信息
     * 
     * @param id 健康评估主键
     * @return 结果
     */
    @Override
    public int deleteHealthAssessmentById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 上传体检报告
     * @param idCardNo
     * @param file
     * @return
     */

    @Override
    public AjaxResult upload(String idCardNo, MultipartFile file) {
        try {
            String url = aliyunOSSService.upload(file.getBytes(), file.getOriginalFilename());

            String text= PDFUtil.pdfToString(file.getInputStream());

            String key=HEALTH_ASSESSMENT_KEY+idCardNo;
            redisTemplate.opsForValue().set(key,text);

            //封装返回结果
            AjaxResult ajaxResult = AjaxResult.success();
            ajaxResult.put("url",url);
            ajaxResult.put("fileName",url);
            ajaxResult.put("originalFilename",file.getOriginalFilename());

            return ajaxResult;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
